#include <iostream>
#include <cstring>
#include <map>
#include <fstream>
#include "../cppGzip/DecodeGzip.h"
#include "../cppGzip/EncodeGzip.h"
#include <math.h>
#include <sstream>
#include <fstream>
#include <stdlib.h>
#include "../Include/VectorTile.h"
using namespace std;


// gzDecompress: do the decompressing
int gzDecompress(const char *src, int srcLen, const char *dst, int dstLen)
{
    z_stream strm;
    strm.zalloc = NULL;
    strm.zfree = NULL;
    strm.opaque = NULL;

    strm.avail_in = srcLen;
    strm.avail_out = dstLen;
    strm.next_in = (Bytef *)src;
    strm.next_out = (Bytef *)dst;

    int err = -1;
    err = inflateInit2(&strm, MAX_WBITS + 16);
    if (err == Z_OK)
    {
        err = inflate(&strm, Z_FINISH);
        if (err == Z_STREAM_END)
        {
            err = strm.total_out;
        }
    }
    inflateEnd(&strm);
    return err;
}

int ReadAndDecodeGzip(const char *filename, std::string &out)
{
    std::ifstream file(filename, std::ios::in | std::ios::binary);
    if (file)
    {
        file.seekg(0, std::ios::end);
        std::streampos length = file.tellg();
        std::vector<char> vc(length);
        file.seekg(0, std::ios::beg);
        file.read(vc.data(), length);
        file.close();
        out.resize(vc.size() * 2);
        int nL = gzDecompress(vc.data(), vc.size(), out.data(), out.size());
        out.resize(nL > 0 ? nL : 0);
    }
    return out.size();
}

int ReadFileContents(const char *filename, int binaryMode, std::string &contentOut)
{
	contentOut = "";
	std::ios_base::openmode mode = (std::ios_base::openmode)0;
	if(binaryMode)
		mode ^= std::ios::binary;
	std::ifstream file(filename, mode);
	if(!file) return 0;

	file.seekg(0,std::ios::end);
	std::streampos length = file.tellg();
	file.seekg(0,std::ios::beg);

	std::vector<char> buffer(length);
	file.read(&buffer[0],length);

	contentOut.assign(&buffer[0], length);
	return 1;
}

///This class would need to be expanded to store features as needed.
class ExampleDataStore : public DecodeVectorTileResults
{
public:
	ExampleDataStore() : DecodeVectorTileResults()
	{
		cout << "Create custom data store..." << endl;
	}

	void Feature(int typeEnum, bool hasId, unsigned long long id, 
		const std::map<std::string, std::string> &tagMap,
		std::vector<Point2D> &pointsOut, 
		std::vector<std::vector<Point2D> > &linesOut,
		std::vector<Polygon2D> &polygonsOut)
	{
		//In real use, delete this function call and add your own functionality.
		DecodeVectorTileResults::Feature(typeEnum, hasId, id, 
			tagMap, pointsOut, linesOut, polygonsOut);
	}
};

int main(int argc, char *argv[])
{
	// Verify that the version of the library that we linked against is
	// compatible with the version of the headers we compiled against.
	GOOGLE_PROTOBUF_VERIFY_VERSION;

	//Note, this file is generated by example.cpp
	//Alternatively: curl "https://api.mapbox.com/v4/mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v7/14/9613/6757.mvt?style=mapbox://styles/mapbox/streets-v10@00&access_token=your-access-token" > map.vector.pbf
	//or curl https://api.mapbox.com/v4/mapbox.mapbox-streets-v7/14/9613/6757.vector.pbf?access_token=your-access-token > map.vector.pbf
	std::string finaIn = "map.vector.pbf";
	if (argc >= 2)
		finaIn = argv[1];
	std::string finaOut = "mapout.vector.pbf";
	if (argc >= 3)
		finaOut = argv[2];
    int tileZoom = 11;
    int tileColumn = 1686;
    int tileRow = 1271;

    string tileData;
    ReadAndDecodeGzip(finaIn.c_str(), tileData);

	//Decode vector data to stdout
	class ExampleDataStore results;
	class DecodeVectorTile vectorDec(tileZoom, tileColumn, tileRow, results);
	vectorDec.DecodeTileData(tileData);

	//Reencode data to file
	std::stringstream binData;
	class EncodeVectorTile vectorEnc(tileZoom, tileColumn, tileRow, binData);
	class DecodeVectorTile vectorDec2(tileZoom, tileColumn, tileRow, vectorEnc);
	vectorDec2.DecodeTileData(tileData);
	std::string uncompData = binData.str();

	//Compress with gzip
	std::filebuf fb2;
	std::filebuf* ret2 = fb2.open(finaOut.c_str(), std::ios::out | std::ios::binary);
	if (ret2 == NULL)
		cout << "Error opening output file" << endl;
	EncodeGzip *enc = new EncodeGzip(fb2, Z_DEFAULT_COMPRESSION);
	enc->sputn(uncompData.c_str(), uncompData.length());
	delete enc; //Needed to complete the gzip file
	fb2.close();

	// Optional:  Delete all global objects allocated by libprotobuf.
	google::protobuf::ShutdownProtobufLibrary();
    getchar();
}

